package au.org.htd.youslide.ppt;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherClientAnchorRecord;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.hslf.model.PPFont;
import org.apache.poi.hslf.model.Picture;
import org.apache.poi.hslf.model.Shape;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.model.TextBox;
import org.apache.poi.hslf.model.TextRun;
import org.apache.poi.hslf.model.TextShape;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.Document;
import org.apache.poi.hslf.record.PPDrawing;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.StyleTextPropAtom;
import org.apache.poi.hslf.record.TextCharsAtom;
import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.hslf.usermodel.SlideShow;

import au.org.htd.youslide.Background;
import au.org.htd.youslide.PMF;
import au.org.htd.youslide.SongProject;
import au.org.htd.youslide.Util;

public class PPTSong implements Song {
   public static final int TOP = 0;
   public static final int BOTTOM = 1;
   public static final int FULL = 2;

   private SlideShow ppt;
   private DisplayText name;
   private DisplayText lyrics;
   private Long bgId;

   private static final Logger log = Logger.getLogger(PPTSong.class.getName());

   public PPTSong(SongProject pProject) {
      name = pProject.getName();
      lyrics = pProject.getLyrics();
      bgId = pProject.getBackgroundId();
   }

   private void init() {
      ppt = new SlideShow();
      Slide slide = ppt.createSlide();
      setBackground(bgId, slide);

      String englishName = name.getEnglishText();
      String chineseName = name.getChineseText();

      if (null != englishName) {
         addTextShape(slide, englishName, name.getEnglishFont(),
               TextBox.AlignCenter, 80, false, new RectangleA(0, 100, 720, 100));
      }
      if (null != chineseName) {
         addTextShape(slide, chineseName, name.getChineseFont(),
               TextBox.AlignCenter, 80, false, new RectangleA(0, 200, 720, 100));
      }
   }

   private void setBackground(Long pPictureId, Slide slide) {
      Background pic = (Background) PMF.getPM().getObjectById(Background.class,
            pPictureId);
      try {
         int id = ppt.addPicture(pic.getBackground(), pic.getPicType());
         Picture pict = new Picture(id);
         // slide.addShape(pict);

         PPDrawing ppdrawing = slide.getSheetContainer().getPPDrawing();

         EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing
               .getEscherRecords()[0];
         EscherContainerRecord spgr = (EscherContainerRecord) Shape
               .getEscherChild(dgContainer,
                     EscherContainerRecord.SPGR_CONTAINER);
         spgr.addChildRecord(pict.getSpContainer());

         pict.setSheet(slide);
         pict.setShapeId(slide.allocateShapeId());

         EscherBSERecord bse = getEscherBSERecord(pict);
         bse.setRef(bse.getRef() + 1);

         setAnchor(pict, new RectangleA(0, 0, 720, 540));

      } catch (IOException e) {
         log.info("Error adding background picture");
      }
   }

   @SuppressWarnings("unchecked")
   private EscherBSERecord getEscherBSERecord(Picture pic) {
      Document doc = ppt.getDocumentRecord();
      EscherContainerRecord dggContainer = doc.getPPDrawingGroup()
            .getDggContainer();
      EscherContainerRecord bstore = (EscherContainerRecord) Shape
            .getEscherChild(dggContainer,
                  EscherContainerRecord.BSTORE_CONTAINER);

      List lst = bstore.getChildRecords();

      int idx = pic.getPictureIndex();
      if (idx == 0) {
         return null;
      } else {
         return (EscherBSERecord) lst.get(idx - 1);
      }
   }

   private void addTextShape(Slide pSlide, String pContent, String pFont,
         int pAlignment, int pSize, boolean pBold, RectangleA pAnchor) {
      TextShape shape = new TextBox();
      shape.setSheet(pSlide);
      shape.setVerticalAlignment(TextBox.AnchorMiddle);
      TextRun run = shape.getTextRun();
      StyleTextPropAtom styleAtom = null;
      TextCharsAtom textAtom = null;
      for (Record r : run.getRecords()) {
         if (r instanceof StyleTextPropAtom)
            styleAtom = (StyleTextPropAtom) r;
         else if (r instanceof TextCharsAtom)
            textAtom = (TextCharsAtom) r;
      }

      styleAtom.getParagraphStyles().clear();
      styleAtom.getCharacterStyles().clear();

      StringBuffer text = new StringBuffer();
      TextPropCollection prProps, chProps;
      RichTextRun rt;
      String chunk = pContent;
      text.append(chunk);

      prProps = styleAtom.addParagraphTextPropCollection(chunk.length());
      chProps = styleAtom.addCharacterTextPropCollection(chunk.length());
      rt = new RichTextRun(shape.getTextRun(), text.length(), chunk.length(),
            prProps, chProps, false, false);
      rt.supplySlideShow(ppt);
      PPFont font = new PPFont();
      font.setFontName(pFont);
      int fontIndex = ppt.addFont(font);
      rt.setFontIndex(fontIndex);
      rt.setFontSize(pSize);
      rt.setAlignment(pAlignment);
      rt.setBold(pBold);

      styleAtom.addParagraphTextPropCollection(1);
      styleAtom.addCharacterTextPropCollection(1);

      String txt = text.toString();
      textAtom.setText(txt);
      ((TextRun) shape.getTextRun()).buildRichTextRuns(styleAtom
            .getParagraphStyles(), styleAtom.getCharacterStyles(), txt);

      setAnchor(shape, pAnchor);

      pSlide.addShape(shape);
   }

   private void setAnchor(Shape shape, RectangleA pAnchor) {
      EscherClientAnchorRecord rec = (EscherClientAnchorRecord) Shape
            .getEscherChild(shape.getSpContainer(),
                  EscherClientAnchorRecord.RECORD_ID);
      rec
            .setFlag((short) (pAnchor.getY() * Shape.MASTER_DPI / Shape.POINT_DPI));
      rec
            .setCol1((short) (pAnchor.getX() * Shape.MASTER_DPI / Shape.POINT_DPI));
      rec.setDx1((short) (((pAnchor.getWidth() + pAnchor.getX())
            * Shape.MASTER_DPI / Shape.POINT_DPI)));
      rec.setRow1((short) (((pAnchor.getHeight() + pAnchor.getY())
            * Shape.MASTER_DPI / Shape.POINT_DPI)));
   }

   private void addSlide(DisplayText pText, Long bgId) {
      Slide slide = ppt.createSlide();
      setBackground(bgId, slide);

      String englishContent = pText.getEnglishText();
      String chineseContent = pText.getChineseText();

      if (null != englishContent) {
         StringTokenizer tok = new StringTokenizer(englishContent.trim(),
               (char) 13 + "");
         int size = DisplayText.getEnglishFontSize(tok.countTokens());
         addBody(englishContent, slide, pText.getEnglishFont(), size, false,
               null == chineseContent ? FULL : TOP);
      }
      if (null != chineseContent) {
         StringTokenizer tok = new StringTokenizer(chineseContent.trim(),
               (char) 13 + "");
         int size = DisplayText.getEnglishFontSize(tok.countTokens());
         addBody(chineseContent, slide, pText.getChineseFont(), size, true,
               null == englishContent ? FULL : BOTTOM);
      }
   }

   private void addFooter(Slide pSlide, int pPage) {
      String content = name.getEnglishText()
            + (name.getEnglishText().length() > 0 ? " / " : "")
            + name.getChineseText() + " - Page " + pPage + " of "
            + ppt.getSlides().length;
      addTextShape(pSlide, content, "SimSun", TextBox.AlignRight, 14, false,
            new RectangleA(0, 520, 720, 20));
   }

   private void addBody(String pContent, Slide pSlide, String pFontName,
         int pFontSize, boolean pBold, int pPosition) {
      switch (pPosition) {
      case FULL:
         addTextShape(pSlide, pContent, pFontName, TextBox.AlignCenter,
               pFontSize, pBold, new RectangleA(0, 0, 720, 520));
         break;
      case TOP:
         addTextShape(pSlide, pContent, pFontName, TextBox.AlignCenter,
               pFontSize, pBold, new RectangleA(0, 0, 720, 260));
         break;
      case BOTTOM:
         addTextShape(pSlide, pContent, pFontName, TextBox.AlignCenter,
               pFontSize, pBold, new RectangleA(0, 260, 720, 260));
      }
   }

   public SlideShow getSlides() {
      init();
      StringTokenizer englishTok = Util.getTok(lyrics.getEnglishText());
      StringTokenizer chineseTok = Util.getTok(lyrics.getChineseText());
      while (englishTok.hasMoreTokens() && chineseTok.hasMoreTokens()) {
         addSlide(new DisplayText(englishTok.nextToken().trim(), chineseTok
               .nextToken().trim()), bgId);
      }
      while (englishTok.hasMoreTokens()) {
         addSlide(new DisplayText(englishTok.nextToken().trim(), null), bgId);
      }
      while (chineseTok.hasMoreTokens()) {
         addSlide(new DisplayText(null, chineseTok.nextToken().trim()), bgId);
      }

      addAllFooters();
      return ppt;
   }

   private void addAllFooters() {
      Slide[] slides = ppt.getSlides();
      for (int i = 1; i < slides.length; i++) {
         addFooter(slides[i], i + 1);
      }
   }

   public SlideShow getEnglishOnly() {
      init();
      StringTokenizer tok = Util.getTok(lyrics.getEnglishText());
      while (tok.hasMoreTokens()) {
         String lyrics = tok.nextToken().trim();
         if (tok.hasMoreTokens()) {
            lyrics += tok.nextToken();
         }
         addSlide(new DisplayText(lyrics, null), bgId);
      }
      addAllFooters();
      return ppt;
   }

   public SlideShow getChineseOnly() {
      init();
      StringTokenizer tok = Util.getTok(lyrics.getChineseText());
      while (tok.hasMoreTokens()) {
         String lyrics = tok.nextToken().trim();
         if (tok.hasMoreTokens()) {
            lyrics += tok.nextToken();
         }
         addSlide(new DisplayText(null, lyrics), bgId);
      }
      addAllFooters();
      return ppt;
   }

   @Override
   public void writeChineseOnly(OutputStream o) throws IOException {
      getChineseOnly().write(o);
   }

   @Override
   public void writeEnglishOnly(OutputStream o) throws IOException {
      getEnglishOnly().write(o);
   }

   @Override
   public void writeSlides(OutputStream o) throws IOException {
      getSlides().write(o);
   }

   @Override
   public String getContentType() {
      return "application/octet-stream";
   }

   @Override
   public String getExtension() {
      return ".ppt";
   }

   @Override
   public void writeLyricsPdf(OutputStream o) {
      // TODO Auto-generated method stub

   }
}
